package com.example.service;

import com.example.config.SftpConfig;
import com.jcraft.jsch.*;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.io.*;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;
import java.util.Vector;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * SFTP服务类（简化版）
 * 只支持流式读取远程文件，不下载到本地
 */
@Slf4j
@Service
public class SftpService {
    
    @Resource
    private SftpConfig sftpConfig;
    
    private Session session;
    private ChannelSftp channelSftp;
    
    /**
     * 连接SFTP服务器
     */
    public void connect() throws JSchException {
        // 检查配置是否注入成功
        if (sftpConfig == null) {
            throw new IllegalStateException("SftpConfig未正确注入，请检查配置类");
        }
        
        // 检查配置参数是否为空
        if (sftpConfig.getHost() == null || sftpConfig.getHost().isEmpty()) {
            throw new IllegalArgumentException("SFTP host未配置，请检查application.yml");
        }
        if (sftpConfig.getUsername() == null || sftpConfig.getUsername().isEmpty()) {
            throw new IllegalArgumentException("SFTP username未配置，请检查application.yml");
        }
        if (sftpConfig.getPassword() == null || sftpConfig.getPassword().isEmpty()) {
            throw new IllegalArgumentException("SFTP password未配置，请检查application.yml");
        }
        
        log.info("开始连接SFTP服务器：{}:{}", sftpConfig.getHost(), sftpConfig.getPort());
        log.debug("SFTP用户名：{}", sftpConfig.getUsername());
        
        JSch jsch = new JSch();
        
        // 创建会话
        log.debug("正在创建SFTP会话...");
        session = jsch.getSession(
            sftpConfig.getUsername(), 
            sftpConfig.getHost(), 
            sftpConfig.getPort()
        );
        
        // 检查session是否创建成功
        if (session == null) {
            throw new IllegalStateException("JSch创建session失败，返回null");
        }
        log.debug("SFTP会话创建成功，session对象：{}", session);
        session.setPassword(sftpConfig.getPassword());
        
        // 配置属性
        Properties config = new Properties();
        config.put("StrictHostKeyChecking", "no");  // 不进行主机密钥检查
        session.setConfig(config);
        session.setTimeout(sftpConfig.getTimeout());
        
        // 连接
        session.connect();
        log.info("SFTP会话连接成功");
        
        // 打开SFTP通道
        Channel channel = session.openChannel("sftp");
        channel.connect();
        channelSftp = (ChannelSftp) channel;
        
        // 验证连接状态
        if (channelSftp.isConnected() && session.isConnected()) {
            log.info("✅ SFTP通道打开成功，连接状态正常");
            log.debug("Session连接状态：{}", session.isConnected());
            log.debug("Channel连接状态：{}", channelSftp.isConnected());
        } else {
            throw new IllegalStateException("SFTP连接失败：通道或会话未正常连接");
        }
    }
    
    /**
     * 验证SFTP连接是否正常
     * @return true=连接正常，false=连接断开
     */
    public boolean isConnected() {
        boolean sessionConnected = session != null && session.isConnected();
        boolean channelConnected = channelSftp != null && channelSftp.isConnected();
        
        log.debug("SFTP连接状态检查 - Session: {}, Channel: {}", sessionConnected, channelConnected);
        
        return sessionConnected && channelConnected;
    }
    
    /**
     * 获取连接状态详情
     */
    public String getConnectionStatus() {
        boolean sessionConnected = session != null && session.isConnected();
        boolean channelConnected = channelSftp != null && channelSftp.isConnected();
        
        return String.format("SFTP连接状态 [Session: %s, Channel: %s, 整体: %s]",
            sessionConnected ? "✅连接" : "❌断开",
            channelConnected ? "✅连接" : "❌断开",
            (sessionConnected && channelConnected) ? "✅正常" : "❌异常"
        );
    }
    
    /**
     * 断开连接
     */
    public void disconnect() {
        if (channelSftp != null && channelSftp.isConnected()) {
            channelSftp.disconnect();
            log.info("SFTP通道已断开");
        }
        if (session != null && session.isConnected()) {
            session.disconnect();
            log.info("SFTP会话已断开");
        }
    }
    
    /**
     * 列出远程目录下的所有文件
     * 
     * @param remotePath 远程目录路径
     * @return 文件列表
     */
    public List<String> listFiles(String remotePath) throws SftpException {
        log.info("列出远程目录文件：{}", remotePath);
        
        List<String> fileList = new ArrayList<>();
        
        @SuppressWarnings("unchecked")
        Vector<ChannelSftp.LsEntry> entries = channelSftp.ls(remotePath);
        
        for (ChannelSftp.LsEntry entry : entries) {
            String fileName = entry.getFilename();
            // 跳过 . 和 ..
            if (!".".equals(fileName) && !"..".equals(fileName)) {
                // 只添加文件，不添加目录
                if (!entry.getAttrs().isDir()) {
                    fileList.add(fileName);
                }
            }
        }
        
        log.info("找到 {} 个文件", fileList.size());
        return fileList;
    }
    
    /**
     * 列出当天的员工数据文件（通过读取.verf验证文件）
     * 逻辑：
     * 1. 找到当天的EMPLOYEE .verf文件
     * 2. 读取.verf文件第一行，格式：NHR_V_PUSH_EMPLOYEE_AIP_U_20251117.dat 1655665 19740 20251117
     * 3. 提取第一个字段（.dat文件名）
     * 4. 返回所有提取到的.dat文件名
     * 
     * @param remotePath 远程目录路径
     * @return 当天的员工数据文件列表
     */
    public List<String> listTodayEmployeeFiles(String remotePath) throws SftpException {
        log.info("========== 列出当天员工数据文件（通过.verf文件） ==========");
        log.info("远程目录：{}", remotePath);
        
        // 获取当前日期（格式：20251117）
        String todayStr = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
        log.info("当前日期：{}", todayStr);
        
        // 提取.verf文件日期的正则：.*EMPLOYEE.*_(\d{8})\.verf
        Pattern verfDatePattern = Pattern.compile(".*EMPLOYEE.*_(\\d{8})\\.verf");
        
        List<String> allFiles = listFiles(remotePath);
        List<String> todayDataFiles = new ArrayList<>();
        
        for (String fileName : allFiles) {
            // 只处理EMPLOYEE的.verf文件
            if (!fileName.contains("EMPLOYEE") || !fileName.endsWith(".verf")) {
                log.debug("跳过非员工verf文件：{}", fileName);
                continue;
            }
            
            // 提取.verf文件名中的日期
            Matcher matcher = verfDatePattern.matcher(fileName);
            if (matcher.matches()) {
                String fileDate = matcher.group(1);
                log.debug(".verf文件：{} -> 日期：{}", fileName, fileDate);
                
                // 只处理当天的.verf文件
                if (todayStr.equals(fileDate)) {
                    log.info("✅ 找到当天.verf文件：{}", fileName);
                    
                    // 读取.verf文件第一行，提取.dat文件名
                    try {
                        String verfFilePath = remotePath + "/" + fileName;
                        String datFileName = readDatFileNameFromVerf(verfFilePath);
                        
                        if (datFileName != null && !datFileName.isEmpty()) {
                            todayDataFiles.add(datFileName);
                            log.info("✅ 从.verf中提取到数据文件：{}", datFileName);
                        } else {
                            log.warn("⚠️ 无法从.verf文件中提取.dat文件名：{}", fileName);
                        }
                        
                    } catch (Exception e) {
                        log.error("❌ 读取.verf文件失败：{} - {}", fileName, e.getMessage());
                    }
                } else {
                    log.debug("跳过非当天.verf文件：{} (文件日期：{})", fileName, fileDate);
                }
            } else {
                log.warn("⚠️ .verf文件名格式不匹配：{}", fileName);
            }
        }
        
        log.info("========== 筛选完成 ==========");
        log.info("总文件数：{}，找到当天.verf文件：{}，提取到数据文件：{}", 
                allFiles.size(), 
                todayDataFiles.size(), 
                todayDataFiles.size());
        
        if (todayDataFiles.isEmpty()) {
            log.warn("⚠️ 未找到当天（{}）的员工数据文件（未找到有效的.verf文件）", todayStr);
        }
        
        return todayDataFiles;
    }
    
    /**
     * 读取.verf文件第一行，提取.dat文件名
     * .verf文件第一行格式：NHR_V_PUSH_EMPLOYEE_AIP_U_20251117.dat 1655665 19740 20251117
     * 
     * @param verfFilePath .verf文件完整路径
     * @return .dat文件名，如：NHR_V_PUSH_EMPLOYEE_AIP_U_20251117.dat
     */
    private String readDatFileNameFromVerf(String verfFilePath) {
        BufferedReader reader = null;
        try {
            // 获取.verf文件流
            InputStream inputStream = channelSftp.get(verfFilePath);
            reader = new BufferedReader(new java.io.InputStreamReader(inputStream, "UTF-8"));
            
            // 读取第一行
            String firstLine = reader.readLine();
            
            if (firstLine != null && !firstLine.trim().isEmpty()) {
                // 提取第一个字段（空格分隔）
                String[] parts = firstLine.trim().split("\\s+");
                if (parts.length > 0) {
                    String datFileName = parts[0];
                    log.debug("从.verf提取到：{}", datFileName);
                    return datFileName;
                }
            }
            
            log.warn("⚠️ .verf文件第一行为空或格式错误：{}", verfFilePath);
            return null;
            
        } catch (Exception e) {
            log.error("❌ 读取.verf文件失败：{} - {}", verfFilePath, e.getMessage());
            return null;
        } finally {
            if (reader != null) {
                try {
                    reader.close();
                } catch (IOException e) {
                    log.warn("关闭reader失败：{}", e.getMessage());
                }
            }
        }
    }
    
    /**
     * 检查远程文件是否存在
     */
    public boolean fileExists(String remoteFilePath) {
        try {
            channelSftp.stat(remoteFilePath);
            return true;
        } catch (SftpException e) {
            return false;
        }
    }
    
    /**
     * 获取文件大小
     */
    public long getFileSize(String remoteFilePath) throws SftpException {
        SftpATTRS attrs = channelSftp.stat(remoteFilePath);
        return attrs.getSize();
    }
    
    /**
     * 获取远程文件的输入流（直接读取，不下载到本地）
     * 
     * @param remoteFilePath 远程文件完整路径
     * @return 文件输入流
     */
    public InputStream getFileInputStream(String remoteFilePath) throws SftpException {
        log.info("打开远程文件流：{}", remoteFilePath);
        return channelSftp.get(remoteFilePath);
    }
    
    /**
     * 读取远程文件到输入流（带文件大小信息）
     * 
     * @param remoteFilePath 远程文件完整路径
     * @return 包含文件流和文件信息的对象
     */
    public RemoteFileStream getRemoteFileStream(String remoteFilePath) throws SftpException {
        log.info("获取远程文件流信息：{}", remoteFilePath);
        
        // 获取文件属性
        SftpATTRS attrs = channelSftp.stat(remoteFilePath);
        
        // 获取输入流
        InputStream inputStream = channelSftp.get(remoteFilePath);
        
        RemoteFileStream fileStream = new RemoteFileStream();
        fileStream.setInputStream(inputStream);
        fileStream.setFileName(extractFileName(remoteFilePath));
        fileStream.setFileSize(attrs.getSize());
        fileStream.setLastModified(attrs.getMTime() * 1000L);
        
        return fileStream;
    }
    
    /**
     * 从完整路径中提取文件名
     */
    private String extractFileName(String filePath) {
        int lastSlash = filePath.lastIndexOf('/');
        return lastSlash >= 0 ? filePath.substring(lastSlash + 1) : filePath;
    }
    
    /**
     * 远程文件流信息类
     */
    public static class RemoteFileStream {
        private InputStream inputStream;
        private String fileName;
        private long fileSize;
        private long lastModified;
        
        public InputStream getInputStream() {
            return inputStream;
        }
        
        public void setInputStream(InputStream inputStream) {
            this.inputStream = inputStream;
        }
        
        public String getFileName() {
            return fileName;
        }
        
        public void setFileName(String fileName) {
            this.fileName = fileName;
        }
        
        public long getFileSize() {
            return fileSize;
        }
        
        public void setFileSize(long fileSize) {
            this.fileSize = fileSize;
        }
        
        public long getLastModified() {
            return lastModified;
        }
        
        public void setLastModified(long lastModified) {
            this.lastModified = lastModified;
        }
    }
}
